helpers.js ➔ cli   B
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 6
c 2
b 0
f 0
nc 6
nop 1
dl 0
loc 39
rs 8.439
1
/*
2
=====fsnip======
3
4
fsnip is a command line utility to extract and modify json from a file.
5
6
*/
7
const fs = require('fs')
8
const os = require('os')
9
const stringify = require('json-stringify-pretty-compact')
10
const chalk = require('chalk')
11
const {runOption} = require('./runOptions.js')
12
13
export function cli (args) {
14
  if (args.length === 3 && args[2] === '--help') {
15
    console.info(chalk.cyan('  fsnip is a tool for extracting json or text snippets from files.\n\n') +
16
                 '  Usage: ' + chalk.whiteBright('fsnip FILE [options [arguments]]\n') +
17
                 '             ' + chalk.gray('process the file and output the result to the console\n\n') +
18
                 '  FILE       ' + chalk.gray('specifies the file to process\n') +
19
                 '  Options:\n' +
20
                 '     --prettify [indent] [maxLength] [margins]\n' +
21
      chalk.grey('             applies prettification to JSON\n') +
22
                 '     --snip JSONPath\n' +
23
      chalk.grey('             snips the key specified by JSONPath\n') +
24
                 '     --ellipsify JSONPath [JSONPath] ... [~JSONPath] ...\n' +
25
      chalk.grey('             replaces the passed json object, array or string with\n' +
26
                 '             ellipses (...) but excludes any keys which follow\n' +
27
                 '             prepended by ~\n' +
28
                 '             eg. fsnip myfile.json --ellipsify $..address ~postcode\n') +
29
                 '    --delKeys JSONPath [JSONPath] ...\n' +
30
      chalk.grey('             deletes the keys following the option\n') +
31
                 '    --from TEXT\n' +
32
                 '    --to TEXT\n' +
33
                 '    --start TEXT\n' +
34
                 '    --finish TEXT\n' +
35
      chalk.grey('             these options work on any plain file extracting the parts of the\n' +
36
                 '             file after or up to the TEXT specified. --from and --to are\n' +
37
                 '             exclusive, whereas --start and --finish are inclusive of the TEXT.')
38
    )
39
  } else if (args.length >= 3) {
40
    try {
41
      var txt = fs.readFileSync(args[2]).toString()
42
    } catch (err) {
43
      console.error(chalk.redBright("unable to read file '" + args[2] + "'"))
44
    }
45
    if (typeof txt !== 'undefined') {
46
      console.info(fsnipDo(args.slice(3), txt))
47
    }
48
  } else { // we couldn't recognise the format on the command line
49
    console.error(chalk.redBright('Unrecognised arguments passed to fsnip. See fsnip --help'))
50
  }
51
}
52
53
function fsnipDo (cmdOpts, inputText) {
54
  // does the processing of the fsnip command
55
  // inputText is the text we want to modify
56
  if (cmdOpts === null || cmdOpts.length === 0) { return inputText } // no processing required as no options passed in
57
  var src = { // a temporary structure containing the text we are working on its type eg. 'json' (which is set later)
58
    text: inputText,
59
    type: '',
60
    outputOptions: {},
61
    error: [],
62
    json: null,
63
    plain: null
64
  }
65
66
  parseOptions()
67
  postProcess(src)
68
  return src.error.length === 0 ? src.text : chalk.redBright(src.error)
69
70
  function parseOptions () {
71
    // now we are going to parse through the options and arguments to extract individual options together with their arguments
72
    var cmdOpt = '' // current option from the cmdOptsString list
73
    var cmdArgs = [] // array containing any arguments for the cmdOpt
74
    for (var i = 0; i < cmdOpts.length; i++) {
75
      if (cmdOpts[i].substr(0, 2) === '--') { // this is a new option eg. --ellipsify
76
        processOption()
77
        cmdOpt = cmdOpts[i] // store the new option we have found
78
        cmdArgs = [] // reset ready for any new arguments
79
      } else {
80
        // this must be an argument for the current option
81
        if (cmdOpt === '') { // error if we don't currently have an option
82
          src.error.push("invalid argument '" + cmdOpts[i] + "' passed without valid option to fsnip")
83
        } else {
84
          cmdArgs.push(cleanCmdOpt(cmdOpts[i]))
85
        }
86
      }
87
    }
88
    processOption()
89
90
    function processOption () {
91
      // process/run any option we've found
92
      if (cmdOpt !== '') {
93
        runOption(cmdOpt, cmdArgs, src)
94
      }
95
    }
96
97
    function cleanCmdOpt (cmdOpt) {
98
      // deals with any special characters dealt with for cross platform purposes
99
      // in windows \$ is replaced with $ as $ is a special character which has to be delimited in posix
100
      let r = cmdOpt
101
      /* istanbul ignore if  */
102
      if (os.platform() === 'win32') {
103
        r = r.replace(/\\\$/g, '$')
104
      }
105
      return r
106
    }
107
  }
108
}
109
110
function postProcess (inpObj) {
111
  // does any post process tidying up
112
  if (inpObj.type === 'json') {
113
    // stringify as required
114
    let opts = inpObj.outputOptions
115
    if (opts.maxLength === 'infinity' && opts.margins === false) {
116
      inpObj.text = JSON.stringify(inpObj.json)
117
    } else if (opts.maxLength === 0 && opts.margins === false) {
118
      inpObj.text = JSON.stringify(inpObj.json, null, opts.indent)
119
    } else {
120
      inpObj.text = stringify(inpObj.json, opts)
121
    }
122
    // now replace any placeholders. The placeholders are valid JSON but what we replace them with may not be valid JSON
123
    inpObj.text = inpObj.text.replace(/\[\s*"fsnipPlaceholderArrEllipses"\s*\]/g, '[...]')
124
    inpObj.text = inpObj.text.replace(/\{\s*"fsnipPlaceholderObj"\s*:\s*"Ellipses"\s*\}/g, '{...}') // do this separately to the one below so that if the object is empty it appears all on one line
125
    inpObj.text = inpObj.text.replace(/"fsnipPlaceholderObj"\s*:\s*"Ellipses"/g, '...')
126
    inpObj.text = inpObj.text.replace(/"fsnipPlaceholderStrEllipses"/g, '"..."')
127
  } else if (inpObj.type === 'plain') {
128
    inpObj.text = inpObj.plain.trim()
129
  }
130
}
131